/*
 * \brief  Lock implementation
 * \author Norman Feske
 * \author Julian Stecklina
 * \date   2007-10-15
 */

/*
 * Copyright (C) 2007-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

/* Genode includes */
#include <base/printf.h>
#include <base/cancelable_lock.h>
#include <cpu/atomic.h>
#include <base/cancelable_lock.h>

namespace Pistachio {
#include <l4/schedule.h>
#include <l4/ipc.h>
}

using namespace Genode;
using namespace Pistachio;

Cancelable_lock::Cancelable_lock(Cancelable_lock::State initial)
: _native_lock(UNLOCKED)
{
	if (initial == LOCKED)
		lock();
}

void Cancelable_lock::lock()
{
	/*
	 * The detection of the cancel-blocking condition relies on the thread's
	 * user-defined handle, which gets reset to zero by core's cancel-blocking
	 * mechanism. Hence, we can initialize the user-defined handle to a magic
	 * (non-zero) value and spin the lock until the value changes.
	 */
	enum { MAGIC_USER_DEFINED_HANDLE = 13 };
	L4_Set_UserDefinedHandle(MAGIC_USER_DEFINED_HANDLE);

	while (!cmpxchg(&_native_lock, UNLOCKED, LOCKED)) {

		/*
		 * XXX We could optimize this by switching to the thread that
		 * owns the lock.
		 *
		 * We could call Yield, but this doesn't work when Core has a
		 * higher priority than other threads, since it will just busy
		 * wait...
		 *
		 * L4_Yield();
		 *
		 * This is the Fiasco-solution with the obvious drawback of
		 * waiting a full time slice even if this is not required.
		 */
		L4_Sleep(L4_TimePeriod(1));

		/* check if sleep was canceled */
		if (L4_UserDefinedHandle() != MAGIC_USER_DEFINED_HANDLE)
			throw Genode::Blocking_canceled();
	}
}

void Cancelable_lock::unlock()
{
	_native_lock = UNLOCKED;
}
